/**
  ******************************************************************************
  * @file    User/main_iap.c 
  * @date    01-Feb-2018
  * @brief   Main program body
  ******************************************************************************
  */  

/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "usart1_printf.h"
#include "systick_delay.h"
#include "gpio_key.h"
#include "usart2_bt.h"
/* Private typedef -----------------------------------------------------------*/
typedef void(*pFunction)(void);
typedef struct{
	// uint8_t cmd;		// 指令码
	uint8_t packAll;	// 数据包总数
	uint8_t packIdx;	// 当前数据包序号
	uint8_t dataSize;	// 数据段长度
	uint8_t data[255];	// 数据段
	// uint8_t evenChk;	// 偶校验
}tBinFrame;
/* Private define ------------------------------------------------------------*/
#define IAP_ADDRESS			0x8000000		// 16 KB
#define APP_ADDRESS			0x8004000		// 48 KB
#define FULL_ADDRESS		0x800FFFF		// 64 KB
#define PAGE_SIZE				0x400		// 1 KB
#define FRAME_CMD				0xEE		// 指令字
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
uint32_t jumpAddress;
pFunction Jump_To_App;
/* Private function prototypes -----------------------------------------------*/
uint8_t Iap_CheckFrame(uint8_t packCnt);
void Iap_CopyFrame(tBinFrame* binFrame);
uint32_t Flash_PagesMask(__IO uint32_t Size);
void Stm32_SoftReset(void);
/* Private functions ---------------------------------------------------------*/

uint8_t Iap_CheckFrame(uint8_t packCnt)
{
	uint16_t sCnt;
	uint8_t bCnt, tmp, evenFlag;
	if(FRAME_CMD != bt_rx_buff[0])
	{

		return 1;
	}
	if(packCnt != bt_rx_buff[2])
	{

		return 2;
	}
	if(bt_rx_len < bt_rx_buff[3]+4)
	{
		return 3;	// 未接收完全
	}
	if(bt_rx_len < bt_rx_buff[3]+4)
	{
		return 4;
	}
	evenFlag = 0;
	for(sCnt = 0; sCnt < bt_rx_len-5; sCnt++)
	{
		tmp = bt_rx_buff[sCnt+4];
		for(bCnt = 0; bCnt < 8; bCnt++)
		{
			if(tmp & 0x01)
			{
				evenFlag = (~evenFlag) & 0x01;
			}
			tmp >>= 1;
		}
	}
	if(evenFlag != bt_rx_buff[bt_rx_len-1])
	{
		return 5;
	}
	return 0;
}

void Iap_CopyFrame(tBinFrame* bf)
{
	uint8_t cnt;
	bf->packAll = bt_rx_buff[1];
	bf->packIdx = bt_rx_buff[2];
	bf->dataSize = bt_rx_buff[3];
	for(cnt = 0; cnt < bf->dataSize; cnt++)
	{
		bf->data[cnt] = bt_rx_buff[cnt+4];
	}		
}

uint32_t Flash_PagesMask(__IO uint32_t Size)
{
	uint32_t pageNbr = 0;
	uint32_t size = Size;
	if((size % PAGE_SIZE) != 0)
	{
		pageNbr = (size / PAGE_SIZE) + 1;
	}
	else
	{
		pageNbr = size / PAGE_SIZE;
	}
	return pageNbr;
}

void Stm32_SoftReset(void)
{
	__set_FAULTMASK(1);		// 关闭所有中断
	NVIC_SystemReset();		// 复位
}

/**
  * @brief  Main program.
  * @param  None
  * @retval None
  */
int main(void)
{
	tBinFrame binFrame;							// 将二进制帧存储到结构体
	uint8_t isNotBinFrame = 0;					// 检验是否为正确的程序下载帧
	uint8_t flashDataPackCnt = 0;				// 已接收到的数据帧数量
	uint32_t allFlashDataSize = 0;				// 整个程序代码内存大小
	uint32_t flashAddress = APP_ADDRESS;		// 分包程序下载地址
	uint32_t pageNbr = 0;						// Flash页数
	uint32_t cnt = 0;							// 循环计数器
	FLASH_Status flashStatus = FLASH_COMPLETE;
	
	FLASH_Unlock();
	
	Systick_Delay_Init();
	Usart1_Printf_Init();
	Gpio_Key_Init();
	Usart2_Bt_Init();

	if(Key0_GetStatus())	// IAP
	{
		printf(">>>IAP Mode\r\n");

		// TODO:等待手机连入后点击升级才擦除APP程序
		while(1)	// 暂时用按钮代替
		{
			// 按键按下时进入更新APP程序模式
			if(Key1_GetStatus())
			{
				// 计算擦除的Flash总共有几页
				pageNbr = Flash_PagesMask(FULL_ADDRESS - APP_ADDRESS);
				printf(">>>pageNbr:%d\r\n", pageNbr);
				// 擦除APP存放地址以后的Flash
				for(cnt = 0; (cnt < pageNbr) && (flashStatus == FLASH_COMPLETE); cnt++)
				{
					// 注意擦除Flash时要按页擦除
					flashStatus = FLASH_ErasePage(APP_ADDRESS + (PAGE_SIZE * cnt));
				}
				break;
			}
		}
		
		while (1)
	  {
			// 接收一包数据写一包数据
			if(bt_rc_flag)
			{
				// 检查串口不定长接收的帧是否出错
				isNotBinFrame = Iap_CheckFrame(flashDataPackCnt);
				printf(">>>isNotBinFrame:%d flashDataPackCnt:%d\r\n", isNotBinFrame, flashDataPackCnt);	
				// for(cnt = 0; cnt < bt_rx_len; cnt++)
				// {
				// 		printf("%c", bt_rx_buff[cnt]);
				// }
				if(isNotBinFrame)
				{
					// 出错处理
					if(isNotBinFrame == 3)	// 未接收完全
					{	
						bt_rc_flag = 0;				// 清除串口接收标志
					}
					else
					{
						bt_rx_len = 0;				// 清除串口接收长度
						bt_rc_flag = 0;				// 清除串口接收标志
						Bt_SendByte(0xFF);		// NACK
					}
				}
				else
				{
					// 处理单包数据
					Iap_CopyFrame(&binFrame);
					// 将数据复制到结构体变量中
					allFlashDataSize += binFrame.dataSize;					
					for(cnt = 0; (cnt < binFrame.dataSize) && (flashAddress < APP_ADDRESS + allFlashDataSize); cnt += 4)
					{
						// 按字写入Flash
						FLASH_ProgramWord(flashAddress, *(uint32_t*)(binFrame.data + cnt));
						// Flash地址偏移4个字节
						flashAddress += 4;
					}
					// printf(">>>flashAddress:%x allFlashDataSize:%d\r\n", flashAddress, allFlashDataSize);
					flashDataPackCnt += 1;
					bt_rx_len = 0;				// 清除串口接收长度
					bt_rc_flag = 0;				// 清除串口接收标志
					Bt_SendByte(0xEE);		// ACK
					if(flashDataPackCnt == binFrame.packAll)
					{
						printf(">>>Reboot!\r\n");
						Stm32_SoftReset();		// 软复位
					}
				}
			}
			// delay_ms(200);
	  }
	}
	else
	{
		// 检测该Flash区域是否已有APP程序
		if(((*(__IO uint32_t*)APP_ADDRESS) & 0x2FFE0000) == 0x20000000)
		{

			printf(">>>APP Mode\r\n");
			jumpAddress = *(__IO uint32_t*)(APP_ADDRESS + 4);
			Jump_To_App = (pFunction)jumpAddress;
			// 设置主堆栈地址
			__set_MSP(*(__IO uint32_t*)APP_ADDRESS);
			// APP中断向量表复位异常处理
			Jump_To_App();
		}
	}
	
  /* Infinite loop */
  while (1)
  {
		printf(">>>IAP-MAIN\r\n");
		// TODO: 加入擦除芯片后烧写失败处理
		delay_ms(200);
  }
}
#ifdef  USE_FULL_ASSERT

/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line)
{ 
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  /* Infinite loop */
  while (1)
  {
  }
}
#endif
